package logparser.ui;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import logparser.AppConfig;
import logparser.bean.CaptureSave;
import logparser.bean.ICaptureHandler;
import logparser.bean.IQueryProcess;
import logparser.bean.ProcessInfo;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteResultHandler;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.LogOutputStream;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Label;


public class LogcatMonitor extends Dialog 
implements ICaptureHandler,IQueryProcess 
{

	protected Object result;
	protected Shell shell;
	private volatile DefaultExecutor m_logcat_process;
	private Button m_btn_logcat;
	private boolean isDisposing = false;
	private Text textDir;
	private Table table;
	private Text textStatus;
	private Label lblStatus;
	private HashMap<Integer,ProcessInfo> m_process_map = new HashMap<Integer, ProcessInfo>();
	private static final Logger logger = LogManager.getLogger(LogcatMonitor.class);
	
	
	/**
	 * Create the dialog.
	 * @param parent
	 * @param style
	 */
	public LogcatMonitor(Shell parent) {
		super(parent, SWT.DIALOG_TRIM);
		setText("Logcat Monitor");
	}

	/**
	 * Open the dialog.
	 * @return the result
	 */
	public Object open() {
		createContents();
		shell.open();
		shell.layout();
		Display display = getParent().getDisplay();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
		return result;
	}

	/**
	 * Create contents of the dialog.
	 */
	private void createContents() {
		shell = new Shell(getParent(), getStyle());
		shell.addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent e) {
				System.out.println("widgetDisposed");
				if(m_logcat_process != null){
					stopLocatMonitor();
					isDisposing = true;
				}
			}
		});
		shell.setSize(574, 422);
		shell.setText(getText());
		
		m_btn_logcat = new Button(shell, SWT.NONE);
		m_btn_logcat.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				new File(getOutputPath()).mkdirs();
				if(m_logcat_process == null){
					try {
						queryProcessInfo();
					} catch (IOException e2) {
						// TODO Auto-generated catch block
						e2.printStackTrace();
					}
					CaptureSave.getInstance().setOutputDir(textDir.getText());
					CaptureSave.getInstance().setCapturehandler(LogcatMonitor.this);
					try {
						CaptureSave.getInstance().StartCapture(LogcatMonitor.this);
					} catch (Exception e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
						UIUtils.showErrorMessageBox(shell,e1.getMessage());
						return;
					}
					startLogcatMonitor();
					if(m_logcat_process != null){
						m_btn_logcat.setText("Stop");
					}
				}else{
					stopLocatMonitor();
				}
			}
		});
		m_btn_logcat.setBounds(20, 72, 538, 25);
		m_btn_logcat.setText("Start Logcat");
		
		textDir = new Text(shell, SWT.BORDER | SWT.READ_ONLY);
		textDir.setBounds(20, 28, 365, 21);
		textDir.setText(AppConfig.getInstance().getOutputDir());
		
		
		Button btnNewButton = new Button(shell, SWT.NONE);
		btnNewButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				File dir = UIUtils.showDirSelectDialog(shell);
				
				if(dir == null){
					return;
				}
				
				textDir.setText(dir.getPath());
				AppConfig.getInstance().setOutputDir(dir.getPath());
				AppConfig.getInstance().saveConfig();
			}
		});
		btnNewButton.setBounds(411, 26, 147, 25);
		btnNewButton.setText("Select output dir");
		
		table = new Table(shell, SWT.BORDER | SWT.FULL_SELECTION);
		table.setBounds(20, 163, 538, 114);
		table.setHeaderVisible(true);
		table.setLinesVisible(true);
		
		Label lblCrcs = new Label(shell, SWT.NONE);
		lblCrcs.setBounds(20, 139, 55, 15);
		lblCrcs.setText("CRCS");
		
		textStatus = new Text(shell, SWT.BORDER | SWT.READ_ONLY | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
		textStatus.setBounds(20, 304, 538, 80);
		
		lblStatus = new Label(shell, SWT.NONE);
		lblStatus.setBounds(20, 283, 55, 15);
		lblStatus.setText("status");
		
		Button btnNewButton_1 = new Button(shell, SWT.NONE);
		btnNewButton_1.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					Process ps = Runtime.getRuntime().exec("adb logcat -c");
					ps.waitFor();
				} catch (IOException e2) {
					// TODO Auto-generated catch block
					e2.printStackTrace();
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}				
			}
		});
		btnNewButton_1.setBounds(20, 103, 538, 25);
		btnNewButton_1.setText("Logcat Clear");
	}
	
	class LineOutputStream extends LogOutputStream{

		@Override
		protected void processLine(String line, int logLevel) {
			// TODO Auto-generated method stub
			handleProcessLine(line);
		}
		
	}
	
	private void stopLocatMonitor(){
		m_logcat_process.getWatchdog().destroyProcess();
		m_logcat_process = null;
		try {
			CaptureSave.getInstance().StopCapture();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
	private void handleLogcatExit(){
		Display.getDefault().asyncExec(new Runnable() {

            public void run() {
            	m_logcat_process = null;
            	if(!isDisposing){
            		m_btn_logcat.setText("Start Logcat");
            	}
            	try {
					CaptureSave.getInstance().StopCapture();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
            }
        });
	}
	
	private void string2ProcessMap(String processes){
		String lines[] = processes.split("\n|\r");
		Matcher matcher;
		String regStr;
		logger.debug("string2ProcessMap");
		m_process_map.clear();
		for(String line:lines){
			regStr = "(\\S+)\\s+(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)";
			matcher = Pattern.compile(regStr).matcher(line);
			if(matcher.find()){
				int pid = Integer.parseInt(matcher.group(2));
				String processName = matcher.group(9);
				logger.debug(String.format("%d - %s",pid,processName));
				m_process_map.put(pid,new ProcessInfo(pid, processName));
			}
			
		}
		
	}
	
	private void queryProcessInfo() throws IOException{
		String inputstring;
		String cmd = "adb shell ps";
		
		Process ps = Runtime.getRuntime().exec(cmd);
		BufferedReader br;  
        StringBuffer sb;  
        String line;


		// input string
		br = new BufferedReader(new InputStreamReader(ps.getInputStream()));  
        sb = new StringBuffer();  
        while ((line = br.readLine()) != null) { 
            sb.append(line).append("\n");  
        }
        inputstring = sb.toString();
        
        
        try {
			int ret = ps.waitFor();
	        if(ret != 0){
	        	throw new IOException(String.format("run %s failed",cmd));
	        }
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        
        string2ProcessMap(inputstring);
	}
	
	
	private void startLogcatMonitor(){

		
		
		CommandLine cmdLine = CommandLine.parse("adb shell logcat -v time");
		final DefaultExecutor executor = new DefaultExecutor();
		PumpStreamHandler streamHandler = new PumpStreamHandler(new LineOutputStream(), System.err);
		final ExecuteResultHandler resultHandler = new ExecuteResultHandler(){

			public void onProcessComplete(int exitValue) {
				// TODO Auto-generated method stub
				System.out.println("onProcessComplete");
				handleLogcatExit();
			}

			public void onProcessFailed(ExecuteException e) {
				// TODO Auto-generated method stub
				System.out.println("onProcessFailed");
				handleLogcatExit();
			}
			
		};
		
		
		executor.setStreamHandler(streamHandler);
		executor.setWatchdog(new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT));
		try {
			executor.execute(cmdLine,resultHandler);
			m_logcat_process = executor;
		} catch (ExecuteException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		textStatus.setText("start logcat monitor....\n");
	}
	
	
	private void handleProcessLine(String line){
		try {
			CaptureSave.getInstance().processLine(line);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
	private String getOutputPath(){
		return textDir.getText();
	}

	public void handleFinished() {
		// TODO Auto-generated method stub
		textStatus.append("logcat monitor finished.\n");
		textStatus.append("The output path is:\n" + CaptureSave.getInstance().getOutputFolder());
	}

	public ProcessInfo getProcessInfo(int pid) {
		// TODO Auto-generated method stub
		ProcessInfo info = m_process_map.get(pid);
		if(info == null){
			logger.debug(String.format("no process name for %d", pid));
			try {
				queryProcessInfo();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			info = m_process_map.get(pid);
			if(info == null){
				return new ProcessInfo(-1, "");
			}
		}
		
		return info;
	}
}
